//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <ddk.h>

EXTERN void DzDelay(Microsecond32 uMicroseconds);

class DelayServer001 : public CDeviceDriver
{
public:
    CARAPI Read(
        /* [in] */ UInt64 u64Offset,
        /* [in] */ UInt32 bytesToRead,
        /* [out] */ EzByteBuf buffer,
        /* [out] */ IEvent * * ppCompletionEvent);

    CARAPI Write(
        /* [in] */ UInt64 u64Offset,
        /* [in] */ EzByteBuf buffer,
        /* [out] */ UInt32 * pBytesWritten,
        /* [out] */ IEvent * * ppCompletionEvent);

    CARAPI Control(
        /* [in] */ Handle32 nControlCode,
        /* [in] */ EzByteBuf inBuffer,
        /* [out] */ EzByteBuf * pOutBuffer,
        /* [out] */ IEvent * * ppCompletionEvent);

    virtual void Dispose() {}
};

EXTERN IDeviceDriver * CDECL CreateDelayServer0(uint_t uDeviceNo, void *pvParameter)
{
    DelayServer001 *pDriver = new DelayServer001;
    if (NULL == pDriver) {
        kprintf("ERROR:There have no enough memory, %s, %d\n",
                        __FILE__, __LINE__);
        return NULL;
    }

    pDriver->AddRef();

    return pDriver;
}

ECode DelayServer001::Read(
    /* [in] */ UInt64 u64Offset,
    /* [in] */ UInt32 bytesToRead,
    /* [out] */ EzByteBuf buffer,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode DelayServer001::Write(
    /* [in] */ UInt64 u64Offset,
    /* [in] */ EzByteBuf buffer,
    /* [out] */ UInt32 * pBytesWritten,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode DelayServer001::Control(
    /* [in] */ Handle32 nControlCode,
    /* [in] */ EzByteBuf inBuffer,
    /* [out] */ EzByteBuf * pOutBuffer,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    if (NULL != ppCompletionEvent || !pOutBuffer->IsNull()) {
        return E_INVALID_ARGUMENT;
    }

    ECode ec = NOERROR;

    switch (nControlCode) {
        case 0:{
            if (inBuffer.IsNull()) {
                return E_INVALID_ARGUMENT;
            }

            uint32_t    uDelayTime = *(UInt32 *)(char *)inBuffer;
            uint32_t    uCurrentTime = DzTicksToMilliseconds(g_uElapsedTicks)
                                                                        * 1000;
            uint32_t    uLeastTime;

            uLeastTime = uCurrentTime + uDelayTime;
            if (uLeastTime < uCurrentTime) {
                uLeastTime = uCurrentTime;
            }

            DzDelay(uDelayTime);

            uCurrentTime = DzTicksToMilliseconds(g_uElapsedTicks) * 1000;
            if (uLeastTime > uCurrentTime) {
                ec = E_FAIL;
            }

            break;
        }

        case 1:{
            if (inBuffer.IsNull()) {
                return E_INVALID_ARGUMENT;
            }

            UInt32    uTimeout = *(UInt32 *)(char *)inBuffer;
            SystemTime  currentTime;
            SystemTime  leastTime;

            DzGetSystemTime(&currentTime);
            leastTime.seconds = currentTime.seconds + uTimeout / 1000;
            if (leastTime.seconds < currentTime.seconds) {
                leastTime.seconds = currentTime.seconds;
            }

            leastTime.microseconds = currentTime.microseconds +
                            (uTimeout / 1000) * 1000;

            if (leastTime.microseconds < currentTime.microseconds) {
                leastTime.microseconds = currentTime.microseconds;
            }

            for (int i = 0; i < 1000; i++) {
                DzDelay(uTimeout);
            }

            DzGetSystemTime(&currentTime);
            if (leastTime.seconds > currentTime.seconds) {
                ec = E_FAIL;
            }
            else if (leastTime.seconds == currentTime.seconds) {
                if (leastTime.microseconds > currentTime.microseconds) {
                    ec = E_FAIL;
                }
            }

            break;
        }

        default:{
            return E_NOT_IMPLEMENTED;
        }
    }

    return ec;
}
